﻿using System;

using Centido.Core;

namespace Centido.Management.ConsoleManager
{
    /// <summary>
    ///   A console command that lists all table permission
    /// filtering by the specified table, principal, data action or access level.
    /// </summary>
    internal class ListPermissionsCommand : PermissionCommandBase
    {
        /// <summary>
        ///   The principal name parameter.
        /// </summary>
        public const string PrincipalNameParameter = "principal";

        /// <summary>
        ///   The principal name parameter (short name).
        /// </summary>
        public const string PrincipalNameShortParameter = "p";

        /// <summary>
        ///   The table name parameter.
        /// </summary>
        public const string TableNameParameter = "table";

        /// <summary>
        ///   The table name parameter (short name).
        /// </summary>
        public const string TableNameShortParameter = "t";

        /// <summary>
        ///   The data action parameter.
        /// </summary>
        public const string DataActionParameter = "action";

        /// <summary>
        ///   The data action parameter (short name).
        /// </summary>
        public const string DataActionShortParameter = "a";

        /// <summary>
        ///   The access level parameter.
        /// </summary>
        public const string AccessLevelParameter = "level";

        /// <summary>
        ///   The access level parameter (short name).
        /// </summary>
        public const string AccessLevelShortParameter = "l";

        /// <summary>
        ///   The display all parameter.
        /// </summary>
        public const string DisplayAllOptionParameter = "all";

        /// <summary>
        ///   The display all parameter (short name).
        /// </summary>
        public const string DisplayAllOptionShortParameter = "i";


        /// <summary>
        ///   Gets the console command name.
        /// </summary>
        public override string Name
        {
            get { return "listpermissions"; }
        }


        /// <summary>
        ///   Gets the console command description.
        /// </summary>
        public override string Description
        {
            get { return Messages.ListPermissionsCommandDescription; }
        }

        
        /// <summary>
        ///   Gets the console command full description.
        /// </summary>
        public override string FullDescription
        {
            get { return Messages.ListPermissionsCommandFullDescription; }
        }


        /// <summary>
        ///   Executes the console command.
        /// </summary>
        /// <param name="context">The console command execution context.</param>
        /// <exception cref="ArgumentNullException">Thrown when the value of <paramref name="context"/> is null.</exception>
        public override void Execute(CommandExecutionContext context)
        {
            var domain = context.CurrentDomain;

            if (domain == null) return;

            // If the current user has no permission to read the role list
            if (!domain.CurrentUser.CanChangeSecurity)
            {
                Console.WriteLine(Messages.NeedCanChangeSecurityPermissionMessage);
                return;
            }

            // The data action filter
            DataAction? dataActionFilter = null;

            // If the data action was specified
            if (context.ParameterValueIsNotEmpty(DataActionParameter, DataActionShortParameter))
            {
                dataActionFilter = GetDataAction(context.GetParameterValue(DataActionParameter, DataActionShortParameter));

                // If the data action was not recognized
                if (!dataActionFilter.HasValue)
                {
                    Console.WriteLine(Messages.DataActionWrongValueError);
                    return;
                }
            }

            // The access level filter
            AccessLevel? accessLevelFilter = null;

            // If the access level was specified
            if (context.ParameterValueIsNotEmpty(AccessLevelParameter, AccessLevelShortParameter))
            {
                accessLevelFilter = GetAccessLevel(context.GetParameterValue(AccessLevelParameter, AccessLevelShortParameter));

                // If the access level was not recognized
                if (!accessLevelFilter.HasValue)
                {
                    Console.WriteLine(Messages.AccessLevelWrongFormatError);
                    return;
                }
            }

            // The principal name filter
            string principalNameFilter = null;

            // If the principal name was specified
            if (context.ParameterValueIsNotEmpty(PrincipalNameParameter, PrincipalNameShortParameter))
            {
                principalNameFilter = context.GetParameterValue(PrincipalNameParameter, PrincipalNameShortParameter);

                // If there is no principal with the specified name
                if (domain.GetPrincipal(principalNameFilter) == null)
                {
                    Console.WriteLine(Messages.CannotFindPrincipalError, principalNameFilter);
                    return;
                }
            }


            // If the table name was specified
            if (context.ParameterValueIsNotEmpty(TableNameParameter, TableNameShortParameter))
            {
                // The target table name
                var tableNameFilter = context.GetParameterValue(TableNameParameter, TableNameShortParameter);

                // Searching for a table with the specified name
                var table = domain.GetTable(tableNameFilter);

                // If there is no table with the specified name
                if (table == null)
                {
                    Console.WriteLine(Messages.CannotFindTableError, tableNameFilter);
                    return;
                }

                DisplayTablePermissions(table, principalNameFilter, dataActionFilter, accessLevelFilter);
            }
            // If the table was not specified
            else
            {
                // Enumerating all tables
                foreach (var table in domain.Tables)
                {
                    // If the all option was specified
                    if (context.GetParameterByName(DisplayAllOptionParameter, DisplayAllOptionShortParameter) != null
                        // ... and the table has the display name
                        && !string.IsNullOrEmpty(table.DisplayName))
                        Console.WriteLine("{0} ({1})", table.Name, table.DisplayName);
                    else
                        Console.WriteLine(table.Name);

                    DisplayTablePermissions(table, principalNameFilter,
                        dataActionFilter, accessLevelFilter, needOffset: true);

                    Console.WriteLine();
                }
            }
        }


        /// <summary>
        ///   Displays permissions for the specified table.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="principalNameFilter">The principal name filter.</param>
        /// <param name="dataActionFilter">The data action filter</param>
        /// <param name="accessLevelFilter">The access level filter.</param>
        /// <param name="needOffset">The need to make an offset.</param>
        private void DisplayTablePermissions(Table table,
            string principalNameFilter, DataAction? dataActionFilter,
            AccessLevel? accessLevelFilter, bool needOffset = false)
        {
            // Enumerating table permissions
            foreach (var perm in table.Permissions)
            {
                // If the principal filter is enables and it is not the target principal
                if (principalNameFilter != null && perm.Principal.Name != principalNameFilter)
                    continue;

                // If the data action filter is enabled and it is not the target data action
                if (dataActionFilter != null && perm.DataAction != dataActionFilter)
                    continue;

                // If the access level filter is enabled and it is not the target access level
                if (accessLevelFilter != null && perm.AccessLevel != accessLevelFilter)
                    continue;

                Console.WriteLine("{0}{1}: {2} {3}",
                    needOffset ? "     " : string.Empty, 
                    perm.Principal.Name,
                    perm.DataAction,
                    perm.AccessLevel);
            }
        }
    }
}
